#coding=utf-8


import subprocess, shlex, time, os, re, sys, urllib2
import tarfile
from xml.dom.minidom import parse
import xml.dom.minidom
import shutil
import threading
import json
import alpha
import copy
from multiprocessing import Process
from multiprocessing import Manager

xmlpathindevice = "/sdcard/window_dump.xml"

"""
param: cmdstring-(执行的指令字符串)， shell-(是否是shell指令), executable-(指令所在的目录)
"""
def commandActuator(cmdstring, shell=True, executable='/bin/sh'):

    if executable == '/bin/bash':
        executable = '/bin/bash'
    print "commandActuator:", cmdstring
    p = subprocess.Popen(cmdstring, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, executable=executable) 
    p.wait()
    output = []
    while True:
        buff = p.stdout.readline() 
        output.append(buff)
        if buff == '' and p.poll() != None:
            break
    return output

"""
dump当前页面的window_dump.xml文件
"""
def __getWindowDump(sn_d):
    dumpfilepath = "./"+sn_d+".xml"
    if os.path.exists(dumpfilepath):
        os.remove(dumpfilepath)
    setScreenOn(sn_d)
    cmdout = commandActuator("adb -s "+ sn_d+" shell uiautomator dump")
    # print cmdout
    cmdout = commandActuator("adb -s "+ sn_d+" pull " + xmlpathindevice +" "+ dumpfilepath)
    # print cmdout
    if "pulled" in cmdout[1] and os.path.exists(dumpfilepath):
        return True, dumpfilepath
    else:
        return False, None

"""
检查屏幕是否点亮
"""
def checkScreenIfOnOrOff(sn_d):

    cmdout = commandActuator("adb -s " + sn_d + " shell dumpsys window policy")
    if '    mAwake=true\n' in cmdout:
        return True
    else:
        return False

def getpackageinfoindevice(sn_d):
    cmdout = commandActuator("adb -s " + sn_d + " shell pm list packages")
    result = {}
    packages = []
    i=0
    for line in cmdout:
        if line == "":
            continue
        print line
        package = {}
        packagename = line.strip("\n").split(":")[1]
        package["key"] = i
        package["value"] = packagename
        packages.append(package)
        i+=1
    result["packages"] = packages
    return result 

"""
点亮屏幕
"""
def setScreenOn(sn_d):
    if not checkScreenIfOnOrOff(sn_d):
        cmdout = commandActuator("adb -s " + sn_d + " shell input keyevent 26")    

"""
熄灭屏幕
"""
def setScreenOff(sn_d):
    if checkScreenIfOnOrOff(sn_d):
        cmdout = commandActuator("adb -s " + sn_d + " shell input keyevent 26")    

"""
解析坐标点
"""
def parseBounds(bounds):
    lefttoppoint, rightbuttompoint = re.findall(r'\[[0-9]+,[0-9]+\]', bounds)
    lefttoppoint = eval(lefttoppoint)
    rightbuttompoint = eval(rightbuttompoint)
    return lefttoppoint[0]+(rightbuttompoint[0]-lefttoppoint[0])//2, lefttoppoint[1]+(rightbuttompoint[1]-lefttoppoint[1])//2

"""
获取resource-id对应的bounds值
"""
def __getPosition(sn_d, atype, value):
    times = 3
    while True:
        result, path = __getWindowDump(sn_d)
        if result:
            domtree = parse(path)
            collection = domtree.documentElement
            nodes = collection.getElementsByTagName("node")
            # print "here resource-id", atype, value, len(nodes)
            for node in nodes:
                if node.hasAttribute("resource-id"):
                    # print "here resource-id", atype, value
                    if atype == 'resource-id' and value == str(node.getAttribute("resource-id")):
                        bounds = node.getAttribute("bounds")
                        return bounds
                if node.hasAttribute("text"):
                    if atype == 'text':
                        # print ">>>>", node.getAttribute("text"), value
                        # print unicode(node.getAttribute("text"))==value, type(unicode(node.getAttribute("text"))), type(unicode(value))
                        if value == unicode(node.getAttribute("text")):
                            bounds = node.getAttribute("bounds")
                            return bounds
            # return ""
        times -= 1
        if times <= 0:
            return ""
        time.sleep(2)

"""
检测对于的resource-id是否存在
"""
def checkElementsIfNotExist(sn_d, type, value):
    if type == 0:
        attr = "resource-id"
    elif type == 1:
        attr = "text"
    else:
        pass
    if "" != __getPosition(sn_d, attr, value):
        return True
    else:
        return False

"""
点击ByResourceId
"""
def click_element_by_ResourceId(sn_d, resourceid, waittime=2):
    
    while True:
        if checkElementsIfNotExist(sn_d, 0, resourceid):
            point = parseBounds(__getPosition(sn_d, "resource-id", resourceid))
            cmdout = commandActuator("adb -s " + sn_d + " shell input tap " + str(point[0]) + " " + str(point[1]))
            break
        else:
            time.sleep(waittime)

def get_packageinfo(sn_d):
    cmdout = commandActuator("adb -s " + sn_d + " shell pm list packages")
    print cmdout
    if "package:com.miui.voiceassist\n" in cmdout:
        return True
    else:
        return False

"""
处理引导页面
"""
def handleGuidePage(sn_d):
    print "handle Guide page now ..."
    print "判断是否存在"
    while True:  
        if checkElementsIfNotExist(sn_d, 0, 'com.android.provision:id/img_miui'):
            break
        time.sleep(10)  
    print '点击>进行引导设置'
    click_element_by_ResourceId(sn_d, "com.android.provision:id/next")
    print '进入语言选择页面，跳过'
    if checkElementsIfNotExist(sn_d, 1, u"设置语言"):
        click_element_by_ResourceId(sn_d, 'com.android.provision:id/next')
    print '进入连接互联网设置界面，跳过'
    if checkElementsIfNotExist(sn_d, 1, u"连接互联网"):
        click_element_by_ResourceId(sn_d, 'com.android.settings:id/provision_btn_extra')
    print '使用条款，点击继续'
    if checkElementsIfNotExist(sn_d, 1, u"使用条款"):
        click_element_by_ResourceId(sn_d, 'com.android.provision:id/next')
    print '检测sim卡，点击继续'
    if checkElementsIfNotExist(sn_d, 1, u"未检测到SIM卡"):
        click_element_by_ResourceId(sn_d, 'com.android.provision:id/next')
    print '设置手机密码，跳过'
    if checkElementsIfNotExist(sn_d, 1, u"设置手机密码"):
        click_element_by_ResourceId(sn_d, 'com.android.provision:id/next')
    print '设置语音唤醒，跳过'
    if checkElementsIfNotExist(sn_d, 1, u"设置语音唤醒"):
        click_element_by_ResourceId(sn_d, 'com.android.provision:id/btn_skip')
    print '其他设置，继续'
    if checkElementsIfNotExist(sn_d, 1, u"其他设置"):
        click_element_by_ResourceId(sn_d, 'com.android.provision:id/next')
    print '个性化风格，继续'
    if checkElementsIfNotExist(sn_d, 1, u"个性化风格"):
        click_element_by_ResourceId(sn_d, 'com.android.thememanager:id/continue_btn')
    print '系统导航方式设置，经典导航键，继续'
    if checkElementsIfNotExist(sn_d, 1, u"系统导航方式"):
        click_element_by_ResourceId(sn_d, 'com.android.provision:id/radio_button_virtual_keys', 5)
        click_element_by_ResourceId(sn_d, 'com.android.provision:id/next')
    print '设置完毕，>'
    if checkElementsIfNotExist(sn_d, 1, u"体验激动人心的MIUI吧!"):
        click_element_by_ResourceId(sn_d, 'com.android.provision:id/next')
    print '检测是否进入mainpage'
    while True:
        if checkElementsIfNotExist(sn_d, 0, 'com.miui.home:id/delete_zone'):
            break
        time.sleep(2)
    return True

class ArgumentsError(Exception):
    pass

"""
刷机
"""
def flashdevice(sn_d, flashscriptpath=None):
    print "flash now ..."
    if flashscriptpath is None:
        return True
    # elif flashscriptpath == "test":
    #     flashscriptpath = "/home/mi/下载/imagesrom/polaris_images_1.1.1_20181116.0000.00_8.0_cn"
    #     if not os.path.exists(flashscriptpath):
    #         raise ArgumentsError(r"test入参是脚本开发这本地的环境，只用于脚本开发这本地测试使用")
    elif not os.path.exists(flashscriptpath):
        raise ArgumentsError(r"rom刷机脚本路径不存在，1.检查你的入参，2.检查是否存在刷机rom")
    print "尝试连接设备并刷机"
    count = 0

    while True:
        cmdout = commandActuator("adb devices")
        index = cmdout.index('List of devices attached\n')
        cmdout = [el for el in cmdout if el not in ['', '\n']][index+1:]
        cout = len(cmdout)
        # devicesno = 0
        tag = False
        for d in cmdout:
            sn, status = d.strip('\n').split('\t')
            print sn, status
            if sn_d == sn:
                if status == "device":
                    cmdout = commandActuator("adb -s " + sn_d + " reboot bootloader")
                    cmdout = commandActuator("cd " + flashscriptpath + "; ./flash_all.sh -s " + sn_d)
                    tag = True
                    break
                else:
                    raise Exception("device is not ready!")
            # devicesno += 1
            # if devicesno >= cout:
            #     raise Exception("device is not exist!")
        if tag:
            break
        count += 1
        if count > 3:
            print "adb devices 无法获取到设备"
            break 
        time.sleep(5)
    
    while True:
        cmdout = commandActuator("adb devices")
        index = cmdout.index('List of devices attached\n')
        cmdout = [el for el in cmdout if el not in ['', '\n']][index+1:]
        if len(cmdout) >= 1 :
            for d in cmdout:
                sn, status = d.strip('\n').split('\t')
                if sn_d == sn:
                    print "after flash..", sn, status
                    if status == "device":
                        print 'flash ok and devies status is ' + status
                        return True
                    # else:
                    #     raise Exception("device is not ready!")
        time.sleep(5)

"""
Monkey测试处理
"""
def handleMonkeyTest(sn_d, commandofmonkeyprogram):
    print "monkey test beginning ..."
    print "command is :" + commandofmonkeyprogram
    commandActuator("adb -s " + sn_d + " shell " + commandofmonkeyprogram)
    commandActuator("adb -s " + sn_d + " pull " + commandofmonkeyprogram.split(">")[1].lstrip(" ").rstrip('"') + " ./" +sn_d +"_"+ str(os.getppid())+"_"+str(os.getpid()) +"_monkey.log")
    print "monkey test over!"
    return True

"""
镜像文件下载
"""
def downloadRomAndTarOpen(urlstr):

    filename = urlstr.split('/')[-1]
    print 'local filename is ' + filename
    uresource = urllib2.urlopen(urlstr)
    fileobjectoflocal = open(filename, 'wb')
    meta = uresource.info()
    filesize = int(meta.getheaders('Content-Length')[0])
    print "Downloading: %s Bytes: %s" % (filename, filesize)
    filesizedownload = 0
    blocksize = 819200
    while True:
        buffer = uresource.read(blocksize)
        if not buffer:
            break
        filesizedownload += len(buffer)
        fileobjectoflocal.write(buffer)
        status = r"%10d  [%3.2f%%]" % (filesizedownload, filesizedownload * 100. / filesize )
        status = status + chr(8)*(len(status)+1)
    print status
    fileobjectoflocal.close()
    dirname = filename.strip('.tgz')
    tarfile.open('./' + filename).extractall('./'+ dirname  )
    targetdirectory = './'+dirname +'/'
    print "===========" + targetdirectory
    countof_ = filename.count('_')
    for i, v in enumerate(filename):
        if v == '_':
            countof_ -= 1
            if countof_ == 0:
                targetdirectory += filename[:i]
    print 'getrom', targetdirectory
    return targetdirectory

def worker(params):
    result = str(os.getppid())+"_"+str(os.getpid())
    print "worker", result, params
    if str(params[4]) == "1":
        print 'build rom 3 times chance'
        # 给三次构建rom包的机会，已防止构建失败和构建超市
        for trytimes in xrange(3):
            print '尝试第%s次构建ROM' % trytimes
            if len(params) < 5:
                status, url = alpha.check_status(request_package_by_shell_cmd(False))
            else:
                status, url = alpha.check_status(alpha.request_package(params[1], params[2], params[3]))
            # status, url = False,  None
            if status and url is not None:
                print '第%s次，构建ROM成功' % trytimes
                break  
        if status:
            url = "http://" + url
            # url = "http://cnbj1-fds.api.xiaomi.net/builder/rom/20181122/polaris_images_1.1.1_20181122.0000.00_8.0_cn_67e8fbd6b8.tgz"
            print "获取rom的url：", url
            # 构建成功，下载并解压到对应目录
            flashscriptpath = downloadRomAndTarOpen(url)
            tims = 0
            # 给两次刷包机会，应对可能存在的刷包问题
            while True:
                if tims > 1:
                    break
                try:
                    # 刷机
                    if flashdevice(params[0], flashscriptpath=flashscriptpath):
                        # 跳过引导页
                        if handleGuidePage(params[0]):
                            result += "1.刷机成功，已跳过引导，进入手机主界面。"
                except Exception, e:
                    print e.message
                    tims += 1  
                    continue
                except xml.parsers.expat.ExpatError, e:
                    print e.message
                    tims += 1
                    continue
            # '清除下载的rom文件和解压缩后的文件'
            romfile = url.split('/')[-1]
            romtarfile = romfile.strip('.tgz')
            print '清除的rom文件和文件夹是：', romfile, romtarfile
            if os.path.exists('./' + romfile):
                os.remove('./' + romfile)
            if os.path.exists('./' + romtarfile):
                shutil.rmtree('./' + romtarfile)
    if str(params[5]) == "1":
        # monkey测试
        if handleMonkeyTest(params[0], params[-2]):
            result += "2. 执行monkey测试结束。"
    if str(params[4]) == "0" and str(params[5]) == "0":
        result += "没有执行任何动作。"
    # print devicesinfo, id(devicesinfo), os.getpid()
    # print params[6][params[0]], id(params[6])
    # 充值使用的机型状态
    devicesinfo[str(params[0])] = "IDLE"
    # 设置任务状态，未使用
    for index, job in enumerate(jobinfo):
        print index, job
        if str(os.getppid())+"_"+str(os.getpid()) in job.keys():
            print jobinfo[index][str(os.getppid())+"_"+str(os.getpid())]
            jobinfo[index] = {str(os.getppid())+"_"+str(os.getpid()): "done"}
    workerinfo = result
    # print workerinfo
    # print jobinfo
    # print devicesinfo[str(params[0])], id(devicesinfo), os.getpid()

"""
计算定时器的时间,输入整数0-23
"""
def timing(params, timestr="0"):
    jobinfo.append({str(os.getppid())+"_"+str(os.getpid()): "running"})
    if len(timestr) == 1:
        timestr = '0' + timestr
    if timestr == '-1':
        timerstarttime = 1
    else:
        # 计算允许距离的时长
        import datetime
        datenow = datetime.datetime.now()
        nexttime = datenow + datetime.timedelta(days=+1)
        nextyear = nexttime.date().year
        nextmonth = nexttime.date().month
        nextday = nexttime.date().day
        nexttime = datetime.datetime.strptime(str(nextyear) + "-" + str(nextmonth) + "-" + str(nextday) + " " + timestr + ":00:00", "%Y-%m-%d %H:%M:%S")
        print nexttime, datenow
        timerstarttime = (nexttime - datenow).total_seconds()
    print timerstarttime
    # 启动定时任务
    timer = threading.Timer(timerstarttime, worker(params))
    timer.start()
    # import calendar
    # print calendar.monthrange(datenow.year, datenow.month)
    # seconds = float(timeparse[0]) * 24 + 


cmd_cn = "http://corgi.pt.miui.com/open-api/buildRom --data-urlencode user=v-wanghaiyue --data-urlencode virant=userdebug --data-urlencode deploy=cn --data-urlencode inputEmail=v-wanghaiyue --data-urlencode inputBuildNumber=9.9.9 --data-urlencode buildLabel=androidSigned --data-urlencode target_file=false --data-urlencode enable_miui_debugging=true --data-urlencode flash_package=true --data-urlencode odex=true --data-urlencode dontCheckChangeBranch=true"
cmd_global = "http://corgi.pt.miui.com/open-api/buildRom --data-urlencode user=v-wanghaiyue --data-urlencode virant=userdebug --data-urlencode deploy=global --data-urlencode inputEmail=v-wanghaiyue --data-urlencode inputBuildNumber=9.9.9 --data-urlencode buildLabel=androidSigned --data-urlencode target_file=false --data-urlencode enable_miui_debugging=true --data-urlencode flash_package=true --data-urlencode odex=true --data-urlencode dontCheckChangeBranch=true"
instance = "curl -X POST %s --data-urlencode model=rolex --data-urlencode profile=mm-dev --data-urlencode fdsRegion=c3"

def request_package_by_shell_cmd(global_tag=False):
    if global_tag:
        instance_get = instance % cmd_global
    else:
        instance_get = instance % cmd_cn
    print instance_get
    for count in xrange(3):
        result = commandActuator(instance_get)
        print result
        result_json = json.loads(result[-2])
        if result_json.get('result') == 'success':
            jobid = result_json.get('id')
            print("JobID: " + str(jobid))
            return jobid
        else:
            print(result_json.get('msg'))
            print 'Jenkins create a job failure. The %s attempt now ...' % str(count + 1)
            continue

def get_connected_devices():
    count = 0
    while True:
        cmdout = commandActuator("adb devices")
        index = cmdout.index('List of devices attached\n')
        cmdout = [el for el in cmdout if el not in ['', '\n']][index+1:]
        print cmdout
        dictofdevice = {}
        if len(cmdout) >= 1:
            for line in cmdout:
                sn, status = line.strip('\n').split('\t')
                dictofdevice[sn]=status
            print dictofdevice
            return dictofdevice
        else:
            count += 1
            if count > 3:
                print "adb devices 无法获取到设备"
                break 
            time.sleep(5)

def get_device_model(sn):
    cmdout = commandActuator("adb -s "+ sn +" shell getprop ro.product.device")
    cmdout = [el for el in cmdout if el not in ['', '\n']]
    return cmdout[0].strip('\n')

"""
执行 python monkey.py "polaris" "o-dev" "cn" '"monkey -p com.android.settings -v -v -v 10000> /sdcard/monkey.log"' -1
 python monkey.py "cn" '"monkey -p com.android.settings -v -v -v 10000> /sdcard/monkey.log"' -1
"""
# worker("ass")
parameters = sys.argv[1:]
print "执行脚本入参:", parameters
# 获取连接到设备上的移动终端设备
dictofdevices = get_connected_devices()
# 定义父子进程共享全局的字典和列表
devicesinfo = Manager().dict() # 管理设备任务状态，IDLE：空闲状态，BUSY：使用状态
jobinfo = Manager().list() # 管理执行的任务的状态， running和done
if dictofdevices is not None:
    # 初始化 devicesinfo 的状态为IDLE
    for key in dictofdevices.keys():
        devicesinfo[key] = "IDLE"
    print devicesinfo
    # 移动终端机型和序列号对应关系
    mapsnmodel = {}
    for device in dictofdevices.keys():
        mapsnmodel[device] = get_device_model(device)
    print mapsnmodel
    workerinfo = ""

"""
处理获取当前连接电脑的移动终端的状态请求
数据结构：{devices: [{product: "polaris_BUSY", sn: "2644fefa"}]}
"""
def get_devices_has_state():
    now = get_connected_devices()
    for d in dictofdevices.keys():
        if d not in now.keys():
            dictofdevices.pop(d)
    for nd in now.keys():
        if nd not in dictofdevices.keys():
            dictofdevices[nd] = "IDLE"
    mapsnmodelinfunc = []
    for device in dictofdevices.keys():
        deviceobject = {}
        print "devicesinfo", devicesinfo, device
        deviceobject["product"] = get_device_model(device) + "_" + devicesinfo[device]
        deviceobject["sn"] = device
        mapsnmodelinfunc.append(deviceobject)
    devices = {}
    devices["devices"] = mapsnmodelinfunc
    return devices

"""
处理走起操作
"""
def execlinemonkey(params):
    print "execlinemonkey start..."
    print devicesinfo, id(devicesinfo), os.getpid()
    print jobinfo
    # 根据界面操作的结果拼接monkey指令
    monkeycmd = "'monkey"
    packages = [] 
    packages.extend(params['pkg'].split(';'))
    packages.extend(params['package'].split(';'))
    # 去重
    packages = [p for p in list(set(packages)) if p != ""]
    print packages
    for pg in packages:
        monkeycmd += " -p " + pg
    print monkeycmd
    monkeycmd += " " + params['loglevel'] + " -s " + params['seed']
    for errignore in params['errhandler'].split(';'):
        monkeycmd += " " + errignore
    if params['pct_touch']:
        monkeycmd += " pct-touch " + params['pct_touch']
    if params['pct_motion']:
        monkeycmd += " pct-motion " + params['pct_motion']
    if params['pct_trackball']:
        monkeycmd += " pct-trackball " + params['pct_trackball']
    if params['pct_nav']:
        monkeycmd += " pct-nav " + params['pct_nav']
    if params['pct_majornav']:
        monkeycmd += " pct-majornav " + params['pct_majornav']
    if params['pct_syskeys']:
        monkeycmd += " pct-syskeys " + params['pct_syskeys']
    if params['pct_appswitch']:
        monkeycmd += " pct-appswitch " + params['pct_appswitch']
    if params['pct_anyevent']:
        monkeycmd += " pct-anyevent " + params['pct_anyevent']
    monkeycmd += " --throttle " + params['throttle'] + " " + params['eventsnumber'] + " > /sdcard/monkey.log'"
    print monkeycmd
    # 如果未选择机型，对IDLE状态的机型进行操作，如果选择机型对制定机型进行操作
    devices = []
    if params['device']:
        for key in devicesinfo.keys():
            if key == params['device'] and devicesinfo[key] != "BUSY":
                devicesinfo[key] = "BUSY"
                devices.append(key)
    else:
        for key in devicesinfo.keys():
            if devicesinfo[key] != "BUSY":
                devicesinfo[key] = "BUSY"
                devices.append(key)
    print devices
    # 构建Process入参，[省略的打包机型， 打包版本， 包适配的区域， 是否需要打包， 是否执行monkey测试， 当前的连接到电脑的设备信息（占时未使用到）， 拼接的monkey测试指令， monkey测试的开始时间]
    argus = ["", params['version'], params['area'], params['build'], params['monkey'], devicesinfo, monkeycmd, '0']
    print argus
    for sn in devices:
        divideparam = copy.deepcopy(argus)
        print sn, mapsnmodel[sn]
        divideparam[0] = mapsnmodel[sn]
        divideparam.insert(0, sn)
        print divideparam
        aprocess = Process(target=timing, args=(divideparam, divideparam[-1],))
        aprocess.start()

# handleGuidePage('2644fefa')


